package com.apestech.framework.file;

import java.io.IOException;
import java.util.ArrayList;

import com.apestech.framework.file.FileDictionary.LogSaveType;
import com.apestech.framework.file.KeyStrategy.KeySaveType;
import com.apestech.framework.file.callback.BufferFileInter;
import com.apestech.framework.file.callback.FileKeyCallBackHandler;
import com.apestech.framework.file.callback.ReadBufferFileCallBackHandler;
import com.apestech.framework.file.callback.WriteBufferFileCallBackHandler;
import com.apestech.framework.file.modul.IndexModul;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.FileUtil;

public class BufferFile extends FileUtil implements BufferFileInter{
	
	public class KeyText<A, B> {  
	    public A text;  
	    public B key;
	    
	    public KeyText(A a, B b) {  
	    	text = a;  
	    	key = b;
	    }  
	}
	
	private int fetchSize=100;  //内存中的记录数
	private BufferFileWrite write=null;
	private BufferFileRead read=null;
	private FileDictionary dict=null;
	private LogSaveType saveType=LogSaveType.COUNT;
	private KeySaveType keySaveType=KeySaveType.Initials;
	private KeyStrategy keyStrategy=null;
	private FileKeyCallBackHandler keyCallBackHandler=null;
	private String saveDir;
	private int rowIndex=-1;
	private String keyField=null;
	private String pinyinField=null;
	private int keyLength=1;
	
	public BufferFile(String saveDir) throws Exception {
		this.saveDir=saveDir;
		iniDict();
	}
	
	public BufferFile(String saveDir, int fetchSize) throws Exception {
		this.saveDir=saveDir;
		this.fetchSize=fetchSize;
		iniDict();
	}
	
	public BufferFile(String saveDir, int fetchSize, LogSaveType saveType) throws Exception {
		this.saveDir=saveDir;
		this.fetchSize=fetchSize;
		this.saveType=saveType;
		iniDict();
	}
	
	public void setFetchSize(int fetchSize){
		this.fetchSize=fetchSize;
		this.dict.setFetchSize(fetchSize);
	}
	
	public int getFetchSize(){
		return this.fetchSize;
	}
	
	public void setSaveType(LogSaveType saveType){
		this.saveType=saveType;
		this.dict.setSaveType(saveType);
	}
	
	public LogSaveType getSaveType(){
		return this.saveType;
	}
	
	public void setSaveDir(){
		this.dict.setsaveDir(saveDir);
	}
	
	public void setKeyField(String keyField){
		this.keyField=keyField;
		iniKeyStrategy();
	}
	
	public String getKeyField(){
		return this.keyField;
	}
	
	public void setPinyinField(String pinyinField){
		this.pinyinField=pinyinField;
		iniKeyStrategy();
	}
	
	public void setKeyLength(int keyLength){
		this.keyLength=keyLength;
		iniKeyStrategy();
	}
	
	public int getKeyLength(){
		return this.keyLength;
	}
	
	public void setFileKeycallBack(FileKeyCallBackHandler keyCallBackHandler){
		this.keyCallBackHandler=keyCallBackHandler;
	}
	
	private void iniDict() throws Exception {
		if(this.fetchSize<1) return;
		if(this.dict==null){
			this.dict=new FileDictionary(this.fetchSize, this.saveType);
			this.dict.setsaveDir(this.saveDir);
		}
	}
	
	public void initIndex() throws Exception{
		JSONObject map=this.dict.initIndexMap();  //initFile();
		setSaveType(this.dict.getSaveType());
		setFetchSize(this.dict.getFetchSize());
		fromJSON(map);
	}
	
	private void iniKeyStrategy(){
		if(this.keyStrategy!=null){
			this.keyStrategy.setKeyField(this.keyField);
			this.keyStrategy.setPinyinField(this.pinyinField);
			this.keyStrategy.setKeyLength(this.keyLength);
			return;
		}
		if(this.keySaveType==null){
			this.keyStrategy=new KeyStrategy();
		} else {
			this.keyStrategy=new KeyStrategy(this.keySaveType);
		}
	}
	
	private KeyText getTextInfo(String text) throws Exception{
		String key=null;
		if(this.saveType==LogSaveType.KEY){
			key=getKey(text);
		}
		return new KeyText<String, String>(text, key);
	}
	
	public void setPrefix(String prefix){
		iniKeyStrategy();
		this.keyStrategy.setPrefix(prefix);
	}
	
	public void setSuffix(String suffix){
		iniKeyStrategy();
		this.keyStrategy.setSuffix(suffix);
	}
	
	private String getKey(String text, boolean value) throws Exception{
		iniKeyStrategy();
		if(this.keyCallBackHandler!=null){
			return this.keyStrategy.getKey(text, this.keyCallBackHandler, value);
		} else {
			return this.keyStrategy.getKey(text, value);
		}
	}
	
	private String getKey(String text) throws Exception{
		return getKey(text, false);
	}
	
	public void setIndex(int rowIndex){
		this.rowIndex=rowIndex;
	}
	
	public int getIndex(){
		return this.rowIndex;
	}
	
	public int getIndex(String keyValue) throws Exception{
		String key=getKey(keyValue, true);
		int start=this.dict.getKeyStart(key);
		int end=this.dict.getKeyEnd(key);
		for(this.rowIndex=start; this.rowIndex<end; this.rowIndex++){
			String text=getText(this.rowIndex);
			String keyval=this.keyStrategy.getRowKeyValue(text);
			if(keyValue.equals(keyval)){
				break;
			}
		}
		return this.rowIndex;
	}
	
	public int fileCount(){
		return this.dict.fileCount();
	}
	
	public String getText() throws Exception{
		return getText(getIndex());
	}
	
	private void getWrite(String file, boolean append) throws Exception{
		if(this.write==null){
			this.write=new BufferFileWrite(file, append);
		}
		if(!this.write.getFileName().equals(file)){
			this.write.close();
		}
		if(this.write.closed()){
			this.write=null;
			this.write=new BufferFileWrite(file, append);
		}
	}
	
	/**
	 * 获取读文件对象
	 * @param file
	 * @throws Exception
	 */
	private void getRead(String file) throws Exception{
		if(this.read==null){
			this.read=new BufferFileRead(file);
		}
		if(!this.read.getFileName().equals(file)){
			if(!this.read.closed()){
				this.read.close();
			}
		}
		if(this.read.closed()){
			this.read=null;
			this.read=new BufferFileRead(file);
		}
	}
	
	private void getRead(String key, int index) throws Exception{
		getRead(FileUtil.getFileNameForSys(this.saveDir+"\\"+key+"\\"+index+".txt"));
	}
	
	private String getFileName(int index, KeyText text) throws Exception{
		if(this.saveType==LogSaveType.COUNT){
			return this.dict.getFilename(index);
		} else {
			return this.dict.getFilename(text, index);
		}
		
	}
	
	private String getFileName(int index) throws Exception{
		return this.dict.getFilename(index);
	}
	
	/**
	 * 接收源数据的写文件操作，支持分多次执行写操作
	 * @param writeBufferFileCallBackHandler
	 * @throws Exception
	 */
	public void write(WriteBufferFileCallBackHandler writeBufferFileCallBackHandler) throws Exception{
		//deleteDir(this.saveDir);
		while(writeBufferFileCallBackHandler.canWrite()){
			String text=writeBufferFileCallBackHandler.write(this.dict.size()-1);
			add(text);
        }
		writeClose();
	}
	
	public void add(String text) throws Exception{
		this.rowIndex++;
		KeyText key=getTextInfo(text);
		if(this.saveType==LogSaveType.KEY){
			this.dict.addRow((String)key.key);
		} else {
			this.dict.addRow();
		}
		addText(text, key);
	}
	
	private void addText(String text, KeyText key) throws Exception{
		getWrite(getFileName(this.rowIndex, key), true);
    	this.write.write(text);
	}
	
	/**
	 * 获取某个索引下的文本信息
	 * @param index
	 * @return
	 * @throws Exception
	 */
	public String getText(int index) throws Exception{
		getRead(getFileName(index));
		IndexModul p=this.dict.getFileIndex(index);
		return this.read.getText(p.getIndex());
	}
	
	public void readKey(String key, ReadBufferFileCallBackHandler readBufferFileCallBackHandler) throws Exception{
		int len=this.dict.getKeyFileCount(key);
		int pos=0;
		if(len==0){
			if(FileUtil.fileExists(this.saveDir+"\\"+key)){
				len=FileUtil.getMaxFileOrder(this.saveDir+"\\"+key)+1;
			}
		}
		for(int i=0; i<len; i++){
			this.getRead(key, i);
			pos=this.read.getFileText(readBufferFileCallBackHandler, pos);
		}
	}
	//getRead(String key, int index)
	
	/**
	 * 从第一行开始读到末尾
	 * @param readBufferFileCallBackHandler
	 * @throws Exception
	 */
	public void read(ReadBufferFileCallBackHandler readBufferFileCallBackHandler) throws Exception{
		this.rowIndex=-1;
		while(this.read.next()){
			readRow(readBufferFileCallBackHandler);
		}
	}
	
	public void read(int page, ReadBufferFileCallBackHandler readBufferFileCallBackHandler) throws Exception{
		int sindex=(page-1)*this.fetchSize+1;
		int eindex=page*this.fetchSize;
		for(int i=sindex; i<=eindex; i++){
			readRow(readBufferFileCallBackHandler);
		}
	}
	
	public void read(String key, ReadBufferFileCallBackHandler readBufferFileCallBackHandler) throws Exception{
		int sindex=this.dict.getKeyStart(key);
		int eindex=this.dict.getKeyEnd(key);
		this.rowIndex=sindex+1;
		getRead(getFileName(sindex+1));
		int j=-1;
		for(int i=sindex; i<eindex; i++){
			j++;
			String text=this.read.getText(j);
			if(text!=null){
				if(this.leftStr(text, 1).equals(",")){
					text=this.rightStr(text, text.length()-1);
				}
				readBufferFileCallBackHandler.read(text, i);
			} else {
				break;
			}
		}
	}
	
	private void readRow(ReadBufferFileCallBackHandler readBufferFileCallBackHandler) throws Exception{
		this.rowIndex++;
		getRead(getFileName(this.rowIndex));
		String text=this.read.getText(this.rowIndex);
		if(this.leftStr(text, 1).equals(",")){
			text=this.rightStr(text, text.length()-1);
		}
		readBufferFileCallBackHandler.read(text, this.rowIndex);
	}
	
	public int size() throws Exception{
		return this.dict.size();
	}
	
	public void writeClose() throws IOException{
		if(this.write!=null && !this.write.closed()){
			this.write.close();
		}
	}
	
	public void readClose() throws IOException{
		if(this.read!=null && !this.read.closed()){
			this.read.close();
		}
	}
	
	public void close()throws IOException{
		writeClose();
		readClose();
	}
	
	public void clear() throws Exception {
		this.rowIndex = -1;
		try {
			close();
			deleteDir(this.saveDir);
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}
	
	public boolean bof(){
		this.rowIndex=-1;
		return true;
	}
	
	public boolean first(){
		if(this.dict.size()<0) return false;
		this.rowIndex=0;
		return true;
	}
	
	public boolean next() throws Exception{
		if(this.rowIndex+1==size()) return false;
		this.rowIndex=this.rowIndex+1;
		return true;
	}
	
	public boolean previous(){
		if(this.rowIndex-1==0) return false;
		this.rowIndex=this.rowIndex-1;
		return true;
	}
	
	public boolean last() throws Exception{
		if(size()<1) return false;
		this.rowIndex=size()-1;
		return true;
	}
	
	public void addPage(int row){
		this.dict.addPage(row);
	}
	
	public void addPage(String key, int row){
		this.dict.addPage(key, row);
	}
	
	public String toString(){
		return this.dict.toString();
	}
	
	public ArrayList<String> getKeyList(){
		return this.dict.getKeyList();
	}
	
	public String getFileIndexString(int index){
		return this.dict.getFileIndexString(index);
	}
	
	public void fromJSON(JSONObject map) throws Exception{
		if(map==null) return;
		this.setKeyField(map.getString("keyField"));
		this.setPinyinField(map.getString("pinyinField"));
		this.setKeyLength(map.getIntValue("keyLength"));
		this.dict.fromJSON(map);
		iniKeyStrategy();
		this.keyStrategy.setKeyField(this.keyField);
		this.keyStrategy.setPinyinField(this.pinyinField);
		this.keyStrategy.setKeyLength(this.keyLength);
	}
	
	public JSONObject toJSON(){
		JSONObject map=this.dict.toJSON();
		map.put("keyField", this.keyField);
		map.put("pinyinField", this.pinyinField);
		map.put("keyLength", this.keyLength);
		return map; 
	}
	
}
